﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Data.Entity;
using System.Linq.Expressions;
using Software41.Tracking.Model;
using LinqKit;
using System.Net;
using Software41.Tracking.Data.BAMWebService;

namespace Software41.Tracking.Data.EF
{
    public class ProcessTraceInfoRepository : IProcessTraceInfoRepository
    {
        private readonly ProcessTraceInfoContext ctx;
        private readonly IBAMRepository bamRepository;

        public ProcessTraceInfoRepository(ProcessTraceInfoContext context, IBAMRepository bamRepository)
        {
            this.ctx = context;
            this.bamRepository = bamRepository;
        }

        #region "IProcessTraceInfoRepository Implementation

        List<ProcessTraceInfo> IProcessTraceInfoRepository.QueryForProcess(Expression<Func<ProcessTraceInfo, bool>> filter,
                                                            Func<IQueryable<ProcessTraceInfo>, IOrderedQueryable<ProcessTraceInfo>> orderBy,
                                                            params Expression<Func<ProcessTraceInfo, object>>[] includes)
        {
            return this.QueryForProcess(filter, orderBy, includes);
        }

        ProcessTraceInfo IProcessTraceInfoRepository.GetProcessInfo(string processInfoActivityID)
        {
            return this.GetProcessTraceInfo(processInfoActivityID);
        }

        StepInfo IProcessTraceInfoRepository.GetStepInfo(string stepInfoActivityID)
        {
            return this.GetStepInfos(new List<string>() { stepInfoActivityID }).FirstOrDefault();
        }

        ActionInfo IProcessTraceInfoRepository.GetActionInfo(string actionInfoActivityID)
        {
            return this.GetActionInfos(new List<string>() { actionInfoActivityID }).FirstOrDefault();
        }

        ExceptionDetail IProcessTraceInfoRepository.GetExceptionDetail(string exceptionDetailActivityID)
        {
            return this.GetExceptionDetail(new List<string>() { exceptionDetailActivityID });
        }

        DocReferenceData IProcessTraceInfoRepository.GetDocReferenceDataDetails(DocReferenceData docReferenceData)
        {
            return this.GetDocReferenceDataDetailsInternal(docReferenceData);
        }


        #endregion

        private List<ProcessTraceInfo> QueryForProcess(Expression<Func<ProcessTraceInfo, bool>> filter = null,
            Func<IQueryable<ProcessTraceInfo>, IOrderedQueryable<ProcessTraceInfo>> orderBy = null,
            params Expression<Func<ProcessTraceInfo, object>>[] includes)
        {
            IQueryable<ProcessTraceInfo> query = this.ctx.ProcessTraceInfos;

            if (filter != null)
            {
                query = query.AsExpandable().Where(filter);
            }

            if (includes != null)
                query = includes.Aggregate(query, (current, include) => current.Include(include));

            if (orderBy != null)
            {
                return orderBy(query).ToList();
            }
            else
            {
                return query.ToList();
            }
        }

        private ProcessTraceInfo GetProcessTraceInfo(string processTraceInfoActivityID)
        {
            ProcessTraceInfo retProcInfo = this.ctx.ProcessTraceInfos.Find(processTraceInfoActivityID);

            var relatedIds = this.GetActivityInstanceIdsForRelatedActivity(Constants.PROCESS_TRACE_INFO, processTraceInfoActivityID);
           
            if ((relatedIds.ProcessTraceInfoIds != null) && (relatedIds.ProcessTraceInfoIds.Count > 0))
            {

                var query = from pi in this.ctx.Set<ProcessTraceInfo>()
                            where relatedIds.ProcessTraceInfoIds.Contains(pi.ActivityID)
                            orderby pi.End
                            select pi;

                retProcInfo.RelatedProcesses = query.ToList();

            }

            retProcInfo.StepInfos = this.GetStepInfos(relatedIds.StepInfoIds);
            retProcInfo.ReferenceData = this.GetReferenceData(Constants.PROCESS_TRACE_INFO, processTraceInfoActivityID);
            retProcInfo.LogDetails = this.GetLogDetails(relatedIds.LogDetailIds);
            return retProcInfo;
        }

        private List<StepInfo> GetStepInfos(List<string> stepInfoIds)
        {
            //if we have a null or empty list, just return an empty List
            if ((stepInfoIds == null) || (stepInfoIds.Count == 0))
                return new List<StepInfo>();

            var query = from si in this.ctx.StepInfos
                        where stepInfoIds.Contains(si.ActivityID)
                        orderby si.End
                        select si;

            var stepInfolist = query.ToList();

            RelatedIds relatedIds = null;
            foreach (StepInfo si in stepInfolist)
            {
                relatedIds = this.GetActivityInstanceIdsForRelatedActivity(Constants.STEP_INFO, si.ActivityID);
                si.ActionInfos = this.GetActionInfos(relatedIds.ActionInfoIds);
                si.ExceptionDetail = this.GetExceptionDetail(relatedIds.ExceptionDetail);
                si.ReferenceData = this.GetReferenceData(Constants.STEP_INFO, si.ActivityID);
            }

            return stepInfolist;

        }

        private List<ActionInfo> GetActionInfos(List<string> actionInfoIds)
        {
            //if we have a null or empty list, just return an empty List
            if ((actionInfoIds == null) || (actionInfoIds.Count == 0))
                return new List<ActionInfo>();

            //otherwise
            var query = from ai in this.ctx.ActionInfos
                        where actionInfoIds.Contains(ai.ActivityID)
                        orderby ai.End
                        select ai;

            var actionInfoList = query.ToList();

            RelatedIds relatedIds = null;
            foreach (ActionInfo ai in actionInfoList)
            {
                relatedIds = this.GetActivityInstanceIdsForRelatedActivity(Constants.ACTION_INFO, ai.ActivityID);
                ai.ExceptionDetail = this.GetExceptionDetail(relatedIds.ExceptionDetail);
                ai.ReferenceData = this.GetReferenceData(Constants.ACTION_INFO, ai.ActivityID);
            }


            return actionInfoList;
        }

        private List<LogDetail> GetLogDetails(List<string> logDetailIds)
        {
            //if we have a null or empty list, just return an empty List
            if ((logDetailIds == null) || (logDetailIds.Count == 0))
                return new List<LogDetail>();

            //otherwise
            var query = from ld in this.ctx.LogDetails
                        where logDetailIds.Contains(ld.ActivityID)
                        orderby ld.CreatedAt
                        select ld;

            return query.ToList();
        }

        private ExceptionDetail GetExceptionDetail(List<string> exceptionDetailIds)
        {
            //if we have a null or empty list, just return an empty List
            if ((exceptionDetailIds == null) || (exceptionDetailIds.Count == 0))
                return null;

            //Otherwise
            IQueryable<ExceptionDetail> query = this.ctx.ExceptionDetails;
            query.Where(s => s.ActivityID == exceptionDetailIds[0]);
            ExceptionDetail ei = query.FirstOrDefault();
            ei.ReferenceData = this.GetReferenceData(Constants.EXCEPTION_DETAIL, ei.ActivityID);

            return ei;

        }

        private List<DocReferenceData> GetReferenceData(string activityName, string activityId)
        {
            var results = this.ctx.Database.SqlQuery<DocReferenceData>(
                                            "EXEC bam_Metadata_GetReferences @activityName, @activityId,@referenceType",
                                            new SqlParameter("activityName", activityName),
                                            new SqlParameter("activityId", activityId),
                                            new SqlParameter("referenceType", "DocumentUrl"));

            return results.ToList();

        }

        private RelatedIds GetActivityInstanceIdsForRelatedActivity(string ActivityName, string activityId)
        {
            Dictionary<string, List<string>>activityIds = this.bamRepository.GetActivityInstanceIdsForRelatedActivity(ActivityName, activityId, Constants.PROCESS_DIAGNOSTICS);

            RelatedIds relatedIds = new RelatedIds
            {
                ProcessTraceInfoIds = (activityIds.ContainsKey(Constants.PROCESS_TRACE_INFO)) ? activityIds[Constants.PROCESS_TRACE_INFO] : null,
                StepInfoIds = (activityIds.ContainsKey(Constants.STEP_INFO)) ? activityIds[Constants.STEP_INFO] : null,
                ActionInfoIds = (activityIds.ContainsKey(Constants.ACTION_INFO)) ? activityIds[Constants.ACTION_INFO] : null,
                LogDetailIds = (activityIds.ContainsKey(Constants.LOG_DETAIL)) ? activityIds[Constants.LOG_DETAIL] : null,
                ExceptionDetail = (activityIds.ContainsKey(Constants.EXCEPTION_DETAIL)) ? activityIds[Constants.EXCEPTION_DETAIL] : null,
            };

            return relatedIds;
        }

        private List<string> GetRelatedActivityInstances(string activityName, string activityId)
        {
            SqlCommand dbCommand = this.ctx.Database.Connection.CreateCommand() as SqlCommand;
            dbCommand.CommandText = "bam_Metadata_GetRelatedActivityInstances";
            dbCommand.CommandType = CommandType.StoredProcedure;
            dbCommand.Parameters.AddWithValue("@viewName", "ProcessDiagnostics" );
            dbCommand.Parameters.AddWithValue("@activityName", activityName);
            dbCommand.Parameters.AddWithValue("@activityId", activityId);

            var relatedIds = new List<string>();

            try
            {
                this.ctx.Database.Connection.Open();

                using (SqlDataReader rdr = dbCommand.ExecuteReader())
                {
                    if (rdr.HasRows)
                        while (rdr.Read())
                            relatedIds.Add(rdr.GetString(1));
                }
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                this.ctx.Database.Connection.Close();
            }

            return relatedIds;
        }


        private DocReferenceData GetDocReferenceDataDetailsInternal(DocReferenceData docReferenceDataToQuery)
        {
            docReferenceDataToQuery.DocumentContent = this.bamRepository.GetReferenceData(docReferenceDataToQuery.ReferenceData);
            
            return docReferenceDataToQuery;
        }

    }


}
